#pragma once
#include <queue>
#include <pthread.h>
#include "Cond.hpp"
#include "Mutex.hpp"

using namespace CondModule;
using namespace MutexModule;

#define DEFAULT_CAPACITY 10

template <typename T>
class BlockingQueue
{
public:
    BlockingQueue(const unsigned int capacity = DEFAULT_CAPACITY)
        : _capacity(capacity), _mutex(Mutex::RECURSIVE)
    {}
    ~BlockingQueue()
    {}

    void push(const T &data)
    {
        LockGuard lock(_mutex);
        while (full())
        {
            _not_full.wait(_mutex);
        }
        _queue.push(data);
        _not_empty.signal();
    }

    T pop()
    {
        T ret;
        {
            LockGuard lock(_mutex);
            while (empty())
            {
                _not_empty.wait(_mutex);
            }
            ret = _queue.front();
            _queue.pop();
            _not_full.signal();
        }
        return ret;
    }

    unsigned int size()
    {
        unsigned int size;
        {
            LockGuard lock(_mutex);
            size = _queue.size();
        }
        return size;
    }

    bool empty()
    {
        bool ret;
        {
            LockGuard lock(_mutex);
            ret = _queue.empty();
        }
        return ret;
    }

    bool full()
    {
        bool ret;
        {
            LockGuard lock(_mutex);
            ret = (_queue.size() == _capacity);
        }
        return ret;
    }

private:
    std::queue<T> _queue;
    unsigned int _capacity;
    Mutex _mutex;
    Cond _not_empty;
    Cond _not_full;
};